#include "FunctionGenerator.h"

#include "MetaGenerator.h"
#include "HeadFile.h"

#include "TypeGenerator.h"

CFunctionGenerator::CFunctionGenerator(CHeadFile *InContext)
{
    Context = InContext;
    Result = std::make_shared<CFunctionInfo>();
}

void CFunctionGenerator::Generate()
{
    //skip RFUNCTION
    {
        auto Token = Context->GetCurrentToken();
        if(Token->Type == ETokenType::RFUNCTION)
        {
            Context->GotoNextToken();
        }
        else
        {
            Context->RaiseError(Token, U"expect 'RFUNCTION'");
        }
    }

    //read meta 
    {
        auto MetaGenerator = std::make_shared<CMetaGenerator>(Context);
        MetaGenerator->Generate();
        Result->Meta = MetaGenerator->Result;
    }

    //read all token until '('
    {
        std::vector<std::shared_ptr<CToken>> Tokens;

        {
            auto Token = Context->GetCurrentToken();
            while(Token->Type != ETokenType::LeftParentheses)
            {
                Tokens.push_back(Token);

                Context->GotoNextToken();
                Token = Context->GetCurrentToken();
            }

        }
        
        Result->Name = Tokens.back()->ToString().ToUtf8();
        Tokens.pop_back();

        auto ReturnTypeGenerator = std::make_shared<CTypeGenerator>(Context, Tokens);
        ReturnTypeGenerator->Generate();
        Result->ReturnType = ReturnTypeGenerator->Result;
        Result->IsStatic = ReturnTypeGenerator->bStatic;
        Result->IsVirtual = ReturnTypeGenerator->bVirtual;

        //skip '('
        Context->GotoNextToken();

        //read parameters
        {
            auto Token = Context->GetCurrentToken();
            while(Token->Type != ETokenType::RightParentheses)
            {
                std::vector<std::shared_ptr<CToken>> TypeTokens;
                bool TreatAsType = true;
                //read type
                {
                    while(true)
                    {
                        auto TypeToken = Context->GetCurrentToken();

                        if(TypeToken->Type == ETokenType::Comma || TypeToken->Type == ETokenType::RightParentheses)
                        {
                            TypeTokens.pop_back(); //remove parameter name

                            TreatAsType = true;
                            
                            auto TypeGenerator = std::make_shared<CTypeGenerator>(Context, TypeTokens);
                            TypeGenerator->Generate();
                            auto Type = TypeGenerator->Result;

                            Result->ParameterTypes.push_back(Type);
                            Result->DecoratedParameterTypes.push_back(TypeGenerator->DecoratedResult);

                            //skip ','
                            if(TypeToken->Type != ETokenType::RightParentheses)
                            {
                                Context->GotoNextToken();
                            }
                            break;
                        }
                        else
                        {
                            if(TypeToken->Type == ETokenType::Assign)
                            {
                                TreatAsType = false;
                            }

                            if(TreatAsType)
                            {
                                TypeTokens.push_back(TypeToken);
                            }
                            Context->GotoNextToken();
                        }

                    }

                }

                Token = Context->GetCurrentToken();
            }
        }


    }

    //skip ')'
    {
        auto Token = Context->GetCurrentToken();
        if(Token->Type == ETokenType::RightParentheses)
        {
            Context->GotoNextToken();
        }
        else
        {
            Context->RaiseError(Token, U"expect ')'");
        }
    }

    //try to read const
    {
        auto Token = Context->GetCurrentToken();
        if(Token->Type == ETokenType::Const)
        {
            Result->IsConst = true;
            Context->GotoNextToken();
        }
    }
}
